#include "stdafx.h"
#include "Scene.h"
#include "Sphere.h"
#include "Plane.h"
#include "Triangle.h"
#include "AreaLight.h"
#include "PointLight.h"

namespace PhotonMapping
{

Scene::Scene() : m_Materials(MaxNumMaterials)
{
}


ScenePtr Scene::Load(const tchar* p_Filename)
{
	using namespace std;
	using namespace Math;

	// open file
	tifstream fileStream(p_Filename);
	if(!fileStream.is_open())
	{
		_tprintf(_T("Failed to open file %s\n"), p_Filename);
		return ScenePtr();
	}

	// parse file
	tstring section;
	ScenePtr scene = ScenePtr(new Scene);
	while(fileStream >> section)
	{		
		// get first section character
		if(section.size() > 0)
		{
			// proceed depending on the section ...
			if(section == _T("Camera"))
			{
				// parse params
				if(!scene->ParseCameraParams(fileStream))
					return ScenePtr();
			}
			else if(section.compare(0, 8, _T("Material\0")) == 0)
			{
				// parse params
				if(!scene->ParseMaterialParams(fileStream))
					return ScenePtr();
			}
			else if(section == _T("Sphere"))
			{
				// parse params
				if(!scene->ParseSphereParams(fileStream))
					return ScenePtr();
			}
			else if(section == _T("Plane"))
			{
				// parse params
				if(!scene->ParsePlaneParams(fileStream))
					return ScenePtr();
			}
			else if(section == _T("TriangleList"))
			{
				// parse params
				if(!scene->ParseTriangleListParams(fileStream))
					return ScenePtr();
			}
			else if(section == _T("PointLight"))
			{
				// parse params
				if(!scene->ParsePointLightParams(fileStream))
					return ScenePtr();
			}
			else if(section == _T("AreaLight"))
			{
				// parse params
				if(!scene->ParseAreaLightParams(fileStream))
					return ScenePtr();
			}
			else
			{
				if(section[0] != _T('/'))
				{
					_tprintf(_T("Unknown scene file section %s\n"), section.c_str());
					return ScenePtr();
				}
			}

			// get rid of the rest of the line
			tstring tmp;
			getline(fileStream, tmp);
		}

	}while(!fileStream.eof());


	// additional grid with spheres:
// 	for(int x = 0; x < 8; x++) 
// 		for(int y = 0; y < 7; y++)
// 		{
// 			scene->m_Objects.push_back(ObjectPtr(new Sphere(Math::Vector3(-4.5f + x * 1.5f, -4.3f + y * 1.5f, 10 ), 0.3f, 6)));
// 		}

	return scene;
}

const std::vector<ObjectPtr>& Scene::GetObjects() const
{
	return m_Objects;
}

const std::vector<LightPtr>& Scene::GetLights() const
{
	return m_Lights;
}

const std::vector<MaterialPtr>& Scene::GetMaterials() const
{
	return m_Materials;
}

const Camera& Scene::GetCamera() const
{
	return m_Camera;
}

bool Scene::ParseMaterialParams(tifstream& p_FileStream)
{
	// parse material id
	int matId;
	if(!(p_FileStream >> matId) || p_FileStream.fail() || matId >= MaxNumMaterials)
	{
		_tprintf(_T("Failed to parse material id or material Id greater than %d!\n"), MaxNumMaterials - 1);
		return false;
	}

	// parse material properties
	tstring sceneParam;
	float specularPower = 20.0f;
	Color diffReflection = Color::ZERO;
	Color specReflection = Color::ZERO;
	Color refraction = Color::ZERO;
	float refractionIdx = 1.0f;
	Color absorption = Color::ZERO;
	while(p_FileStream >> sceneParam)
	{
		if(sceneParam.size())
		{			
			if(sceneParam == _T("SpecularPower"))	// specular power
			{
				if(!ParseParamFloat(p_FileStream, specularPower, _T("Material specular power")))
					return false;
			}
			else if(sceneParam == _T("Refraction"))	// refraction
			{
				if(!ParseParamColor(p_FileStream, refraction, _T("Material refraction")))
					return false;
			}
			else if(sceneParam == _T("RefractionIndex"))	// refraction index
			{
				if(!ParseParamFloat(p_FileStream, refractionIdx, _T("Material refraction index")))
					return false;
			}
			else if(sceneParam == _T("DiffReflection"))	// diffuse reflection
			{
				if(!ParseParamColor(p_FileStream, diffReflection, _T("Material diffuse reflection")))
					return false;
			}
			else if(sceneParam == _T("SpecReflection"))	// diffuse reflection
			{
				if(!ParseParamColor(p_FileStream, specReflection, _T("Material specular reflection")))
					return false;
			}
			else if(sceneParam == _T("Absorption"))	// absorption
			{
				if(!ParseParamColor(p_FileStream, absorption, _T("Material absoprtion")))
					return false;
			}
			else
			{
				// end parsing on }
				if(sceneParam[0] == _T('}'))
				{
					// add new material
					m_Materials[matId] = MaterialPtr(new Material(specularPower, diffReflection, specReflection, refraction, refractionIdx, absorption));
					return true;
				}

				// ignore empty lines, { and //
				if(sceneParam[0] != _T('{') && sceneParam[0] != _T('/'))
				{
					_tprintf(_T("Unknown material parameter %s\n"), sceneParam.c_str());
					return false;
				}
			}

			// get rid of the rest of the line
			tstring tmp;
			getline(p_FileStream, tmp);
		}
	}

	_tprintf(_T("Unexpected end of file!\n"));
	return false;
}

bool Scene::ParseSphereParams(tifstream& p_FileStream)
{
	// parse sphere properties
	tstring sceneParam;
	Math::Vector3 center;
	float radius;
	intx matId;
	while(p_FileStream >> sceneParam)
	{
		if(sceneParam.size())
		{			
			if(sceneParam == _T("Center")) // position
			{
				if(!ParseParamVec3(p_FileStream, center, _T("Sphere center")))
					return false;
			}
			else if(sceneParam == _T("Radius"))	// radius
			{
				if(!ParseParamFloat(p_FileStream, radius, _T("Sphere radius")))
					return false;
			}
			else if(sceneParam == _T("Material"))	// material
			{
				if(!ParseParamInt(p_FileStream, matId, _T("Sphere material")))
					return false;
			}
			else
			{
				// end parsing on }
				if(sceneParam[0] == _T('}'))
				{
					// add new sphere
					m_Objects.push_back(ObjectPtr(new Sphere(center, radius, matId)));
					return true;
				}

				// ignore empty lines, { and //
				if(sceneParam[0] != _T('{') && sceneParam[0] != _T('/'))
				{
					_tprintf(_T("Unknown sphere parameter %s\n"), sceneParam.c_str());
					return false;
				}
			}

			// get rid of the rest of the line
			tstring tmp;
			getline(p_FileStream, tmp);
		}
	}

	_tprintf(_T("Unexpected end of file!\n"));
	return false;
}

bool Scene::ParsePlaneParams(tifstream& p_FileStream)
{
	using namespace Math;

	// parse plane properties
	tstring sceneParam;
	Vector3 normal;
	float dist;
	intx matId;
	while(p_FileStream >> sceneParam)
	{
		if(sceneParam.size())
		{			
			if(sceneParam == _T("Normal")) // normal
			{
				if(!ParseParamVec3(p_FileStream, normal, _T("plane normal")))
					return false;
			}
			else if(sceneParam == _T("Distance"))	// distance
			{
				if(!ParseParamFloat(p_FileStream, dist, _T("plane distance")))
					return false;
			}
			else if(sceneParam == _T("Material"))	// material
			{
				if(!ParseParamInt(p_FileStream, matId, _T("plane material")))
					return false;
			}
			else
			{
				// end parsing on }
				if(sceneParam[0] == _T('}'))
				{
					// add new plane
					m_Objects.push_back(ObjectPtr(new Plane(normal, dist, matId)));
					return true;
				}

				// ignore empty lines, { and //
				if(sceneParam[0] != _T('{') && sceneParam[0] != _T('/'))
				{
					_tprintf(_T("Unknown plane parameter %s\n"), sceneParam.c_str());
					return false;
				}
			}

			// get rid of the rest of the line
			tstring tmp;
			getline(p_FileStream, tmp);
		}
	}

	_tprintf(_T("Unexpected end of file!\n"));
	return false;
}

bool Scene::ParseTriangleListParams(tifstream& p_FileStream)
{
	using namespace Math;

	// parse plane properties
	intx matId = -1;
	tstring sceneParam;
	Vector3 coord0, coord1, coord2;
	while(p_FileStream >> sceneParam)
	{
		if(sceneParam.size())
		{			
			if(sceneParam == _T("Material"))	// material
			{
				if(!ParseParamInt(p_FileStream, matId, _T("plane material")))
					return false;
			}
			else if(sceneParam == _T("Triangle"))	// distance
			{
				tchar equalSign = 0;
				if((p_FileStream >> equalSign) && equalSign == _T('='))				
				{
					if(!ParseCoordinates(p_FileStream, coord0, _T("triangle coordinates 0")))
						return false;
					if(!ParseCoordinates(p_FileStream, coord1, _T("triangle coordinates 1")))
						return false;
					if(!ParseCoordinates(p_FileStream, coord2, _T("triangle coordinates 2")))
						return false;

					if(matId == -1)
					{
						_tprintf(_T("Missing material specification in triangle list\n"));
						return false;
					}
					// add new triangle
					m_Objects.push_back(ObjectPtr(new Triangle(coord0, coord1, coord2, matId)));
				}
				else
				{
					_tprintf(_T("Missing = sign after triangle specification\n"));
					return false;
				}
			}
			else
			{
				// end parsing on }
				if(sceneParam[0] == _T('}'))
				{
					return true;
				}

				// ignore empty lines, { and //
				if(sceneParam[0] != _T('{') && sceneParam[0] != _T('/'))
				{
					_tprintf(_T("Unknown plane parameter %s\n"), sceneParam.c_str());
					return false;
				}
			}

			// get rid of the rest of the line
			tstring tmp;
			getline(p_FileStream, tmp);
		}
	}

	_tprintf(_T("Unexpected end of file!\n"));
	return false;
}


bool Scene::ParsePointLightParams(tifstream& p_FileStream)
{
	// parse light properties
	tstring sceneParam;
	Math::Vector3 pos;
	Color color;
	float intensity;
	while(p_FileStream >> sceneParam)
	{
		if(sceneParam.size())
		{			
			if(sceneParam == _T("Position")) // position
			{
				if(!ParseParamVec3(p_FileStream, pos, _T("Light position")))
					return false;
			}
			else if(sceneParam == _T("Intensity"))	// color
			{
				if(!ParseParamFloat(p_FileStream, intensity, _T("Light intensity")))
					return false;
			}
			else if(sceneParam == _T("Color"))	// color
			{
				if(!ParseParamColor(p_FileStream, color, _T("Light Color")))
					return false;
			}
			else
			{
				// end parsing on }
				if(sceneParam[0] == _T('}'))
				{
					// add new light
					m_Lights.push_back(LightPtr(new PointLight(pos, color, intensity)));
					return true;
				}

				// ignore empty lines, { and //
				if(sceneParam[0] != _T('{') && sceneParam[0] != _T('/'))
				{
					_tprintf(_T("Unknown light parameter %s\n"), sceneParam.c_str());
					return false;
				}
			}

			// get rid of the rest of the line
			tstring tmp;
			getline(p_FileStream, tmp);
		}
	}

	_tprintf(_T("Unexpected end of file!\n"));
	return false;
}

bool Scene::ParseAreaLightParams(tifstream& p_FileStream)
{
	// parse light properties
	tstring sceneParam;
	Math::Vector3 p0, p1, p2, p3;
	Color color;
	float intensity;
	while(p_FileStream >> sceneParam)
	{
		if(sceneParam.size())
		{			
			if(sceneParam == _T("P0")) // p0
			{
				if(!ParseParamVec3(p_FileStream, p0, _T("Light P0")))
					return false;
			}
			else if(sceneParam == _T("P1")) // p1
			{
				if(!ParseParamVec3(p_FileStream, p1, _T("Light P1")))
					return false;
			}
			else if(sceneParam == _T("P2")) // p2
			{
				if(!ParseParamVec3(p_FileStream, p2, _T("Light P2")))
					return false;
			}
			else if(sceneParam == _T("P3")) // p3
			{
				if(!ParseParamVec3(p_FileStream, p3, _T("Light P3")))
					return false;
			}
			else if(sceneParam == _T("Intensity"))	// color
			{
				if(!ParseParamFloat(p_FileStream, intensity, _T("Light intensity")))
					return false;
			}
			else if(sceneParam == _T("Color"))	// color
			{
				if(!ParseParamColor(p_FileStream, color, _T("Light Color")))
					return false;
			}
			else
			{
				// end parsing on }
				if(sceneParam[0] == _T('}'))
				{
					// add new light
					m_Lights.push_back(LightPtr(new AreaLight(p0, p1, p2, p3, color, intensity)));
					return true;
				}

				// ignore empty lines, { and //
				if(sceneParam[0] != _T('{') && sceneParam[0] != _T('/'))
				{
					_tprintf(_T("Unknown light parameter %s\n"), sceneParam.c_str());
					return false;
				}
			}

			// get rid of the rest of the line
			tstring tmp;
			getline(p_FileStream, tmp);
		}
	}

	_tprintf(_T("Unexpected end of file!\n"));
	return false;
}

bool Scene::ParseCameraParams(tifstream& p_FileStream)
{
	// parse light properties
	tstring sceneParam;
	Math::Vector3 pos, target;
	while(p_FileStream >> sceneParam)
	{
		if(sceneParam.size())
		{			
			if(sceneParam == _T("Position")) // position
			{
				if(!ParseParamVec3(p_FileStream, pos, _T("Camera position")))
					return false;
			}
			else if(sceneParam == _T("Target"))	// target
			{
				if(!ParseParamVec3(p_FileStream, target, _T("Camera target")))
					return false;
			}
			else
			{
				// end parsing on }
				if(sceneParam[0] == _T('}'))
				{
					// set camera parameters
					m_Camera.SetPosition(pos);
					m_Camera.SetTarget(target);
					m_Camera.UpdateMatrix();
					return true;
				}

				// ignore empty lines, { and //
				if(sceneParam[0] != _T('{') && sceneParam[0] != _T('/'))
				{
					_tprintf(_T("Unknown camera parameter %s\n"), sceneParam.c_str());
					return false;
				}
			}

			// get rid of the rest of the line
			tstring tmp;
			getline(p_FileStream, tmp);
		}
	}

	_tprintf(_T("Unexpected end of file!\n"));
	return false;
}

bool Scene::ParseParamInt(tifstream& p_FileStream, intx& p_Value, const tchar* p_ParamName)
{
	bool result = false;
	tchar equalSign;
	if((p_FileStream >> equalSign) && equalSign == _T('='))
	{
		p_FileStream >> p_Value;
		if(!p_FileStream.fail())
			result = true;
	}

	if(result == false)
		_tprintf(_T("Failed to parse %s\n"), p_ParamName);
	return result;

}

bool Scene::ParseParamFloat(tifstream& p_FileStream, float& p_Value, const tchar* p_ParamName)
{
	bool result = false;
	tchar equalSign;
	if((p_FileStream >> equalSign) && equalSign == _T('='))
	{
		p_FileStream >> p_Value;
		if(!p_FileStream.fail())
			result = true;
	}

	if(result == false)
		_tprintf(_T("Failed to parse %s\n"), p_ParamName);
	return result;
}

bool Scene::ParseParamVec3(tifstream& p_FileStream, Math::Vector3& p_Value, const tchar* p_ParamName)
{
	bool result = false;
	tchar equalSign;

	if((p_FileStream >> equalSign) && equalSign == _T('='))
	{
		tchar seperator;
		p_FileStream >> p_Value.m_X >> seperator;
		if(!p_FileStream.fail() && seperator == _T(','))
		{
			p_FileStream >> p_Value.m_Y >> seperator;
			if(!p_FileStream.fail() && seperator == _T(','))
			{
				p_FileStream >> p_Value.m_Z;
				if(!p_FileStream.fail())
					result = true;
			}
		}
	}

	if(result == false)
		_tprintf(_T("Failed to parse %s\n"), p_ParamName);
	return result;
}

bool Scene::ParseCoordinates(tifstream& p_FileStream, Math::Vector3& p_Value, const tchar* p_ParamName)
{
	bool result = false;
	tchar bracket;

	if((p_FileStream >> bracket) && bracket == _T('('))
	{
		tchar seperator;
		p_FileStream >> p_Value.m_X >> seperator;
		if(!p_FileStream.fail() && seperator == _T(','))
		{
			p_FileStream >> p_Value.m_Y >> seperator;
			if(!p_FileStream.fail() && seperator == _T(','))
			{
				p_FileStream >> p_Value.m_Z >> bracket;
				if(!p_FileStream.fail() && bracket == _T(')'))
					result = true;
			}
		}
	}

	if(result == false)
		_tprintf(_T("Failed to parse %s\n"), p_ParamName);
	return result;
}

bool Scene::ParseParamColor(tifstream& p_FileStream, Color& p_Value, const tchar* p_ParamName)
{
	bool result = false;
	tchar equalSign;
	
	if((p_FileStream >> equalSign) && equalSign == _T('='))
	{
		tchar seperator;
		p_FileStream >> p_Value.m_R >> seperator;
		if(!p_FileStream.fail() && seperator == _T(','))
		{
			p_FileStream >> p_Value.m_G >> seperator;
			if(!p_FileStream.fail() && seperator == _T(','))
			{
				p_FileStream >> p_Value.m_B;
				if(!p_FileStream.fail())
					result = true;
			}
		}
	}

	if(result == false)
		_tprintf(_T("Failed to parse %s\n"), p_ParamName);
	return result;
}

} // PhotonMapping
